package com.ld.admin.shiro.service.impl;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringJoiner;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.stereotype.Service;

import com.ld.admin.config.SystemStaticParams;
import com.ld.admin.model.system.SysMenuModel;
import com.ld.admin.model.system.SysRoleModel;
import com.ld.admin.plugin.cache.PermCacheUtil;
import com.ld.admin.shiro.service.IShiroService;
import com.ld.common.util.SharedVarsUtil;
import com.ld.shieldsb.common.composition.util.web.Global;
import com.ld.shieldsb.common.core.collections.MapUtils;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.util.SpringContextHolder;
import com.ld.shieldsb.common.core.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ShiroServiceImpl implements IShiroService {
    protected String adminPath = Global.PROJECT_ADMIN_S;
    protected String frontPath = Global.PROJECT_FRONT_S;

    @Override
    public Map<String, String> loadFilterChainDefinitionMap() {
        boolean kickoutEnable = PropertiesModel.CONFIG.getBoolean("shiro.kickout.enable", false);
        StringBuilder extendFilter = new StringBuilder("");
        if (kickoutEnable) {
            extendFilter.append("kickout,");
        }
        // 权限控制map
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置过滤:不会被拦截的链接 -> 放行 start ----------------------------------------------------------
        // 放行Swagger2页面，需要放行这些
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/**", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");

        // 登陆
        filterChainDefinitionMap.put("/api/auth/login/**", "anon");
        // 开放登陆接口
        filterChainDefinitionMap.put(adminPath + "/login", "anon");
        // 页面跳转
        filterChainDefinitionMap.put(adminPath + "/to/a/**", "anon"); // a/to/a/loginDialog
        // 三方登录
        filterChainDefinitionMap.put("/api/auth/loginByQQ", "anon");
        filterChainDefinitionMap.put("/api/auth/afterlogin.do", "anon");
        // 退出
        filterChainDefinitionMap.put("/api/auth/logout", "anon");
        // 放行未授权接口，重定向使用
        filterChainDefinitionMap.put("/api/auth/unauth", "anon");
        // token过期接口
        filterChainDefinitionMap.put("/api/auth/tokenExpired", "anon");
        // 被挤下线
        filterChainDefinitionMap.put("/api/auth/downline", "anon");
        // 放行 end ----------------------------------------------------------

        // 从数据库或缓存中查取出来的url与resources对应则不会被拦截 放行
        if (!PermCacheUtil.isLoaded()) { // 未加载先加载
            PermCacheUtil.reloadCache();
        }
        Map<String, SysMenuModel> permMap = PermCacheUtil.getAllPermMap();
        if (MapUtils.isNotEmpty(permMap)) {
            // 排序
            Map<String, String> urlRolesPerm = new HashMap<>();

            permMap.keySet().forEach(permId -> {
                if (StringUtils.isNotBlank(permId)) {
                    SysMenuModel permModel = permMap.get(permId);
                    if (StringUtils.isEmpty(permModel.getHref())) {
                        permModel.setHref("system_href_" + permModel.getPageId() + "_" + System.currentTimeMillis());
                        System.out.println(permModel);
                    }
                    String url = permModel.getHref();
                    String ctxPath = (String) SharedVarsUtil.getSharedVars("ctxPath"); // 项目路径
                    if (StringUtils.isNotEmpty(ctxPath)) {
                        url = url.replace(ctxPath, "");
                    }
                    if (permModel.getPermType() == SystemStaticParams.PERMISSION_CHECK) { // 需要校验权限

                        // 根据url查询相关联的角色名,拼接自定义的角色权限
                        List<SysRoleModel> roleList = permModel.getPermRoles();
                        StringJoiner zqRoles = new StringJoiner(",", "zqRoles[", "]");
                        if (!CollectionUtils.isEmpty(roleList)) {
                            roleList.forEach(f -> {
                                zqRoles.add(f.getName());
                            });
                        } else {
                            zqRoles.add("_superManager"); // 未分配角色的超级管理员
                        }

                        // 注意过滤器配置顺序不能颠倒
                        // ① 认证登录
                        // ② 认证自定义的token过滤器 - 判断token是否有效
                        // ③ 角色权限 zqRoles：自定义的只需要满足其中一个角色即可访问 ; roles[admin,guest] : 默认需要每个参数满足才算通过，相当于hasAllRoles()方法
                        // ④ zqPerms:认证自定义的url过滤器拦截权限 【注：多个过滤器用 , 分割】
//                    filterChainDefinitionMap.put( "/api" + e.getUrl(),"authc,token,roles[admin,guest],zqPerms[" + e.getResources() + "]" );
//                        filterChainDefinitionMap.put(url, "authc," + zqRoles.toString());

                        urlRolesPerm.put(url, extendFilter.toString() + "authc," + zqRoles.toString());
//                        filterChainDefinitionMap.put("/api/system/user/listPage", "authc,token,zqPerms[user1]"); // 写死的一种用法
                    } else if (permModel.getPermType() == SystemStaticParams.PERMISSION_LOGIN_NO_CHECK) { // 登录后无需权限
//                        filterChainDefinitionMap.put(url, "user"); // 前者(authc)是认证过(手动登录)，后者(user)是登录过（可能自动登录，如rememberMe）
                        urlRolesPerm.put(url, extendFilter.toString() + "authc");
                    } else {
//                        filterChainDefinitionMap.put(url, "anon");
                        urlRolesPerm.put(url, "anon");
                    }

                }
            });
            // 排序
            Map<String, String> sortedUrlRolesPerm = urlRolesPerm.entrySet().stream()
                    .sorted((Entry<String, String> o1, Entry<String, String> o2) -> {
                        // 按长度，长的在前
                        int diff = o2.getKey().length() - o1.getKey().length();
                        return diff; // 负数o1在前正数o1在后
                    }).collect(
                            Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oleValue, newValue) -> oleValue, LinkedHashMap::new));
            System.out.println("unsorted=====" + urlRolesPerm.keySet());
            System.out.println("  sorted=====" + sortedUrlRolesPerm.keySet());
            System.out.println("  sorted=====" + sortedUrlRolesPerm);

            filterChainDefinitionMap.putAll(sortedUrlRolesPerm);
        }
        // ⑤ 认证登录
//        filterChainDefinitionMap.put("/**", "authc");
        // 主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
        filterChainDefinitionMap.put(adminPath + "/**", extendFilter.toString() + "authc");
        return filterChainDefinitionMap;

    }

    @Override
    public void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean, Integer roleId, Boolean isRemoveSession) {
        synchronized (this) {
            AbstractShiroFilter shiroFilter;
            if (shiroFilterFactoryBean == null) {
                shiroFilterFactoryBean = SpringContextHolder.getBean(ShiroFilterFactoryBean.class); // 不传则获取
            }
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

            // 清空拦截管理器中的存储
            manager.getFilterChains().clear();
            // 清空拦截工厂中的存储,如果不清空这里,还会把之前的带进去
            // ps:如果仅仅是更新的话,可以根据这里的 map 遍历数据修改,重新整理好权限再一起添加
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            // 动态查询数据库中所有权限
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitionMap());
            // 重新构建生成拦截
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                manager.createChain(entry.getKey(), entry.getValue());
            }
            log.debug("--------------- 动态生成url权限成功！ ---------------");

            // 动态更新该角色相关联的用户shiro权限
            if (roleId != null) {
                updatePermissionByRoleId(roleId, isRemoveSession);
            }
        }
    }

    @Override
    public void updatePermissionByRoleId(Integer roleId, Boolean isRemoveSession) {
        // 查询当前角色的用户shiro缓存信息 -> 实现动态权限
        /*List<User> userList = userMapper.selectUserByRoleId(roleId);
        // 删除当前角色关联的用户缓存信息,用户再次访问接口时会重新授权 ; isRemoveSession为true时删除Session -> 即强制用户退出
        if (!CollectionUtils.isEmpty(userList)) {
            for (User user : userList) {
                ShiroUtils.deleteCache(user.getUsername(), isRemoveSession);
            }
        }*/
        log.debug("--------------- 动态修改角色【" + roleId + "】的用户权限成功！ ---------------");
    }

}
